/*
 * This file is part of quickLoad, a software for gamers
 *
 * Copyright (C) 2005-2008 Lucas Romero
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>.
 */
using System;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml;
using System.IO;
//using System.ComponentModel;

namespace AutoUpdater
{
	/// <summary>
	/// Summary description for Class1.
	/// </summary>
	public class AutoUpdateChecker
	{
        public delegate void UpdateProgressChangedEventHandler(object sender, UpdateEventArgs e);
        public delegate void UpdateCheckCompleteEventHandler(AutoUpdateChecker sender, UpdateCheckEventArgs e);
        public event UpdateProgressChangedEventHandler UpdateProgressChanged;
        public event UpdateCheckCompleteEventHandler UpdateCheckComplete;
        private string m_UpdateFileURL = string.Empty;
        private string m_DownloadedFile = string.Empty;
        private string m_ErrorMessage = string.Empty;
        //private string m_ApplicationDir = string.Empty;
        private bool m_Cancelled=false;
	    private long m_FileSize=0;
        private long m_BytesProcessed = 0;
        private const int BLOCK_SIZE = 4096;
        Byte[] buffer = new Byte[BLOCK_SIZE];
        private string m_UpdateURL = string.Empty;
        Version m_InstalledVersion;
        UsageInfo m_UsageInfo;
        UpdateStatus m_UpdateStatus = UpdateStatus.Idle;
		public AutoUpdateChecker()
		{
            //m_ApplicationDir = ApplicationDir;
		}
        public int HTTPTimeout = 10000;
	    public string DownloadedFile
	    {
	        get
	        {
                return m_DownloadedFile;
	        }
	    }
	    public string ErrorMessage
	    {
	        get
	        {
                return m_ErrorMessage;
	        }
	    }
	    public string UpdateFileURL
	    {
	        get
	        {
                return m_UpdateFileURL;
	        }
	    }
	    public int Progress
	    {
            get
            {
                try
                {
                    float db = (m_BytesProcessed * 100 / m_FileSize);
                    if (db > 100)
                    {
                        db = 100;
                    }
                    return (int)db;
                }
                catch
                {
                    return 0;
                }
            }
	    }
	    public void Cancel()
	    {
	        m_Cancelled = true;
            m_UpdateStatus = UpdateStatus.Failed;
	    }
	    public bool hasUpdate(string UpdateURL,Version InstalledVersion,UsageInfo info)
	    {
            string extendedurl;
            if (info == null)
                extendedurl = UpdateURL;
            else
                extendedurl = UpdateURL + "?i=" + info.UniqueID + "&v=" + info.CurrentVersion;
            string HTML = GetHTML(extendedurl);
            string sRecentVersion = Regex.Match(HTML, "<version>(.*?)</version>").Groups[1].Value;
	        m_UpdateFileURL = Regex.Match(HTML, "<url>(.*?)</url>").Groups[1].Value;
            Version RecentVersion = new Version(sRecentVersion);
	        if(RecentVersion > InstalledVersion)
	            return true;
	        else 
	            return false;
	    }
	    public void hasUpdateAsync(string UpdateURL,Version InstalledVersion,UsageInfo info)
	    {
            if (UpdateCheckComplete == null)
                throw new Exception("You must have the UpdateCheckComplete-event configured to check for an update asynchronously!");
            m_UpdateURL = UpdateURL;
            m_InstalledVersion = InstalledVersion;
	        m_UsageInfo = info;
            ThreadPool.QueueUserWorkItem(new WaitCallback(StartAsyncCheck));
	    }
	    private void StartAsyncCheck(object dummy)
	    {
	        bool hasUpd = false;
	        bool failed = true;
	        try
	        {
                hasUpd = hasUpdate(m_UpdateURL, m_InstalledVersion, m_UsageInfo);
	            failed = false;
	        }
	        catch
	        {
	            failed = true;
	        }
	        UpdateCheckEventArgs ev = new UpdateCheckEventArgs(hasUpd,failed);
            if (UpdateCheckComplete != null)
                UpdateCheckComplete(this, ev);
	    }
	    public void GetUpdateFileAsync(string UpdateFileURL,string TargetDir)
	    {
            m_UpdateStatus = UpdateStatus.Idle;
            m_UpdateFileURL = UpdateFileURL;
            ThreadPool.QueueUserWorkItem(new WaitCallback(StartDownloadAsync), TargetDir);
	    }
	    
	    private void StartDownloadAsync(object sTargetDir)
        {
            Stream answer = null;
            FileStream fs = null;
            HttpWebRequest request;
            HttpWebResponse response = null;
            try
            {
                m_UpdateStatus = UpdateStatus.Downloading;
                DirectoryInfo dir = new DirectoryInfo((string)sTargetDir);
                if (!dir.Exists)
                    dir.Create();

                request = (HttpWebRequest)WebRequest.Create(m_UpdateFileURL);
                request.Timeout = HTTPTimeout;
                response = (HttpWebResponse)request.GetResponse();
                answer = response.GetResponseStream();
                m_FileSize = response.ContentLength;
                int bytesread = 0;
                fs =
                    new FileStream(dir.FullName + Path.DirectorySeparatorChar + "update.zip", FileMode.Create,
                                   FileAccess.Write);
                while ((bytesread = answer.Read(buffer, 0, buffer.Length)) > 0 && !m_Cancelled)
                {
                    fs.Write(buffer, 0, bytesread);
                    m_BytesProcessed += bytesread;
                    if (UpdateProgressChanged != null)
                    {
                        UpdateProgressChanged(this, new UpdateEventArgs(m_UpdateStatus, Progress, QuickLoad.Helpers.getFormattedKiloBytes(m_BytesProcessed)));
                    }
                }
                m_DownloadedFile = fs.Name;
                m_UpdateStatus = UpdateStatus.Complete;
            }
            catch (Exception ex)
            {
                m_UpdateStatus = UpdateStatus.Failed;
                m_ErrorMessage = ex.Message;
            }
            finally
            {
                if (fs != null)
                    fs.Close();
                if (answer != null)
                    answer.Close();
                if (response != null)
                    response.Close();
            }
            UpdateProgressChanged(this, new UpdateEventArgs(m_UpdateStatus, Progress, m_ErrorMessage));
        }
        private string GetHTML(string URL)
        {
            string HTML = String.Empty;
            HttpWebRequest request;
            HttpWebResponse response;
            StreamReader answer = null;
            request = (HttpWebRequest)WebRequest.Create(URL);
            request.Timeout = HTTPTimeout;
            //get response
            response = (HttpWebResponse)request.GetResponse();
            try
            {
                answer = new StreamReader(response.GetResponseStream());
                //while (m_UploadStatus != ItemUploadStatus.Aborted && answer.ReadLine() != String.Empty)
                //{
                //HTML += answer.ReadLine();
                //}
                HTML = answer.ReadToEnd();
            }
            catch
            {
                //just don't crash
            }
            finally
            {
                answer.Close();
            }
            return HTML;
        }
	}
    public class UsageInfo
    {
        public string UniqueID = string.Empty;
        public string CurrentVersion = "Unknown";
        public UsageInfo(string UniqueID,string CurrentVersion)
        {
            this.UniqueID = UniqueID;
            this.CurrentVersion = CurrentVersion;
        }
    }
    public class UpdateCheckEventArgs : EventArgs
    {
        public UpdateCheckEventArgs(bool hasUpdate,bool Failed)
        {
            m_hasUpdate = hasUpdate;
            m_Failed = Failed;
            
        }
        public bool hasUpdate
        {
            get
            {
                return m_hasUpdate;    
            }
        }
        public bool Failed
        {
            get
            {
                return m_Failed;
            }
        }
        
        bool m_hasUpdate=false;
        bool m_Failed=false;
    }
    public class UpdateEventArgs : EventArgs
    {
        public UpdateEventArgs(UpdateStatus Status, int Progress, string ErrorMessage)
        {
            m_UpdateStatus = Status;
            m_Progress = Progress;
            m_ErrorMessage = ErrorMessage;
        }
        private int m_Progress = 0;
        private UpdateStatus m_UpdateStatus = UpdateStatus.Idle;
        private string m_ErrorMessage = string.Empty;
        #region Public get's
        public string ErrorMessage
        {
            get
            {
                return m_ErrorMessage;
            }
        }
        
        public UpdateStatus Status
        {
            get
            {
                return m_UpdateStatus;
            }
        }
        public int Progress
        {
            get
            {
                return m_Progress;
            }
        }
        #endregion
    }
    public enum UpdateStatus
    {
        Idle,
        Checking,
        Downloading,
        Installing,
        Complete,
        Failed
    }
}
